/*
 * NativeRobot.java
 * Copyright (C) 2006 Francisco J. Hernández
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 */
package NativeRobot;

import java.awt.*;
import java.awt.event.InputEvent;
import java.io.*;
import java.util.HashMap;
import java.util.Vector;

/*
 * NativeRobot.java
 *
 * Created on 9 de marzo de 2006, 9:18
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
/**
 * Clase que extiende la clase java.awt.Robot, para a�adir las funcionalidades con las
 *      que se pueda obtener el cursor del raton en cualquier momento de la ejecucion
 *      aunque el puntero no este dentro de nuestra ventana, tambi� averigua que boton
 *      esta pulsado en que momento y que tecla esta pulsada.
 *
 *      Hace uso de codigo nativo por lo que no es multiplataforma, concretamente solo 
 *      funciona bajo windows, si alguien pudiese implementar la libreria dinamica para
 *      las X-Windows siguiendo el esquema de entrada salida adecuada, se agradeceria.
 *
 * @author Francisco J. Hdez Hdez
 */
public class NativeRobot extends Robot {

    /**
     * Crea una nueva instancia de NativeRobot
     */
    public NativeRobot() throws AWTException {
        loadNativeLibrary();
    }

    /**
     * Crea una nueva instancia de NativeRobot
     */
    public NativeRobot(GraphicsDevice screen)
            throws AWTException {
        super(screen);
        loadNativeLibrary();
    }

    /**
     * Crea una nueva instancia de NativeRobot
     */
    public NativeRobot(String windowsTittle) throws AWTException {
        loadNativeLibrary();
        if (findAllTittleWindow(windowsTittle)) {
            System.out.println(java.util.ResourceBundle.getBundle("NativeRobot/NativeRobot").getString("TEXT_WINDOW") + actWinTittle + java.util.ResourceBundle.getBundle("NativeRobot/NativeRobot").getString("TEXT_NATIVE_MODE_ENABLED"));
        } else {
            System.err.println(java.util.ResourceBundle.getBundle("NativeRobot/NativeRobot").getString("WARNING_NOT_FOUND_WINDOW") + windowsTittle + java.util.ResourceBundle.getBundle("NativeRobot/NativeRobot").getString("TEXT_NATIVE_MODE_NOT_ENABLED"));
        }
    }

    /**
     * Carga la libreria dinamica con el codigo nativo adecuado al sistema
     */
    private void loadNativeLibrary() {
        java.io.InputStream in;
        File fich;
        in = getClass().getResourceAsStream(NATIVE_LIBRARY);
        fich = new File(System.getProperty("java.io.tmpdir") + NATIVE_LIBRARY);

        if (!isNative()) {
            System.err.println(java.util.ResourceBundle.getBundle("NativeRobot/NativeRobot").getString("WARNING_NO_NATIVEMODE_IMPLEMENT"));
            return;
        }
        try {
            if (fich.exists()) {
                fich.delete();
            }
            if (!fich.exists()) {
                DataInputStream a = new DataInputStream(in);
                DataOutputStream b = new DataOutputStream(new FileOutputStream(fich));
                byte buff[] = new byte[a.available()];
                a.readFully(buff);
                b.write(buff);
                a.close();
                b.close();
            }
            Runtime.getRuntime().load(fich.getCanonicalPath().toString());

        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return;
    }

    public Color getPixelColor(int x, int y) {
        Color color = null;
        if (actWin && nativeMode) {
            try {
                int tmp[] = nativeGetPixelColor(actWinHandle, x, y);
                color = new Color(tmp[0], tmp[1], tmp[2]);
            } catch (Exception e) {
                actWin = false;
                color = super.getPixelColor(x, y);
            }
        } else {
            color = super.getPixelColor(x, y);
        }
        return color;
    }

    private native int[] nativeGetPixelColor(int actWinHandle, int x, int y);

    public void keyPress(int keyCode) {
        if (actWin && nativeMode) {
            try {
                nativeKeyPress(actWinHandle, keyCode);
                super.delay(super.getAutoDelay());
            } catch (Exception e) {
                actWin = false;
                super.keyPress(keyCode);
            }
        } else {
            super.keyPress(keyCode);
        }
    }

    private native boolean nativeKeyPress(int actWinHandle, int keyCode);

    public void keyRelease(int keyCode) {
        if (actWin && nativeMode) {
            try {
                nativeKeyRelease(actWinHandle, keyCode);
                super.delay(super.getAutoDelay());
            } catch (Exception e) {
                actWin = false;
                super.keyRelease(keyCode);
            }
        } else {
            super.keyRelease(keyCode);
        }
    }

    private native boolean nativeKeyRelease(int i, int j);

    public void mouseMove(int x, int y) {
        if (actWin && nativeMode) {
            try {
                nativeMouseMove(actWinHandle, x, y);
                super.delay(super.getAutoDelay());
            } catch (Exception e) {
                actWin = false;
                super.mouseMove(x, y);
            }
        } else {
            super.mouseMove(x, y);
        }
    }

    private native boolean nativeMouseMove(int i, int j, int k);

    public void mousePress(int button) {
        if (actWin && nativeMode) {
            try {
                switch (button) {
                    case InputEvent.BUTTON1_MASK: // '\020'
                        nativeLeftMousePress(actWinHandle);
                        super.delay(super.getAutoDelay());
                        break;

                    case InputEvent.BUTTON2_MASK: // '\b'
                        nativeRightMousePress(actWinHandle);
                        super.delay(super.getAutoDelay());
                        break;

                    case InputEvent.BUTTON3_MASK: // '\004'
                        nativeMiddleMousePress(actWinHandle);
                        super.delay(super.getAutoDelay());
                        break;

                    default:
                        super.mousePress(button);
                        break;
                }
            } catch (Exception e) {
                actWin = false;
                super.mousePress(button);
            }
        } else {
            super.mousePress(button);
        }
    }

    private native boolean nativeLeftMousePress(int i);

    private native boolean nativeMiddleMousePress(int i);

    private native boolean nativeRightMousePress(int i);

    public void mouseRelease(int button) {
        if (actWin && nativeMode) {
            try {
                switch (button) {
                    case InputEvent.BUTTON1_MASK: // '\020'
                        nativeLeftMouseRelease(actWinHandle);
                        super.delay(super.getAutoDelay());
                        break;

                    case InputEvent.BUTTON2_MASK: // '\b'
                        nativeRightMouseRelease(actWinHandle);
                        super.delay(super.getAutoDelay());
                        break;

                    case InputEvent.BUTTON3_MASK: // '\004'
                        nativeMiddleMouseRelease(actWinHandle);
                        super.delay(super.getAutoDelay());
                        break;

                    default:
                        super.mouseRelease(button);
                        break;
                }
            } catch (Exception e) {
                actWin = false;
                super.mouseRelease(button);
            }
        } else {
            super.mouseRelease(button);
        }
    }

    private native boolean nativeLeftMouseRelease(int i);

    private native boolean nativeMiddleMouseRelease(int i);

    private native boolean nativeRightMouseRelease(int i);

    public boolean findAllTittleWindow(String subTittle) {
        return findAllTittleWindow(subTittle, false);
    }

    public boolean findAllTittleWindow(String subTittle, boolean caseInsensitive) {
        if (caseInsensitive) {
            subTittle = subTittle.toLowerCase();
        }
        int desc[] = getNativeAllWindowHandle();
        CharSequence seq = subTittle.subSequence(0, subTittle.length());
        ventNativas.clear();
        for (int i = 0; i < desc.length; i++) {
            byte tmpCad[] = getNativeWindowTittle(desc[i]);
            if (tmpCad == null) {
                continue;
            }
            String tmpStr = new String(tmpCad);
            if (caseInsensitive) {
                tmpStr = tmpStr.toLowerCase();
            }
            if (tmpStr.contains(seq)) {
                actWinTittle = tmpStr;
                actWinHandle = desc[i];
                actWin = true;
                ventNativas.put(tmpStr, new Integer(desc[i]));
            }
        }

        if (!ventNativas.isEmpty()) {
            return true;
        } else {
            actWin = false;
            actWinTittle = null;
            actWinHandle = 0;
            return false;
        }
    }

    public Object[][] getAllWindowDesc() {
        int desc[] = getNativeAllWindowHandle();
        Vector tittleWin = new Vector();
        Vector controlList = new Vector();
        for (int i = 0; i < desc.length; i++) {
            byte tmpCad[] = getNativeWindowTittle(desc[i]);
            if ((tmpCad == null) || (tmpCad.length <= 0)) {
                continue;
            }
            String tmpStr = new String(tmpCad);
            Object[] obj = new Object[]{new Integer(desc[i]), tmpStr};
            if (!controlList.contains(new Integer(desc[i]))) {
                tittleWin.add(new Object[]{new Integer(desc[i]), tmpStr});
                controlList.add(new Integer(desc[i]));
            }
        }
        Object[][] res = new Object[tittleWin.size()][2];
        tittleWin.copyInto(res);
        return res;
    }

    public int[] getAllWindowHandle() {
        return getNativeAllWindowHandle();
    }

    private native byte[] getNativeWindowTittle(int i);

    private native int[] getNativeAllWindowHandle();

    private native int[] getNativeMousePos();

    private native int[] getNativeMousePosHandle(int i);

    private native int isNativeButtonPressed(int i);

    private native int isNativeKeyPressed(int i);

    public Point getMousePos() {
        int coor[];
        if (actWin && nativeMode) {
            coor = getNativeMousePosHandle(actWinHandle);
        } else {
            coor = getNativeMousePos();
        }
        return new Point(coor[0], coor[1]);
    }

    public boolean isKeyPressed(int codeKey) {
        return isNativeKeyPressed(codeKey) != 0;
    }

    public boolean isButtonPressed(int codeButton) {
        return isNativeButtonPressed(codeButton) != 0;
    }

    public static boolean isWindowSystem() {
        return windowSystem;
    }

    public static boolean isNative() {
        return windowSystem;
    }

    public static HashMap getVentNativas() {
        return ventNativas;
    }

    public static String getActWinTittle() {
        return actWinTittle;
    }

    public static void setActWinTittle(String aActWinTittle) {
        Integer tmp = (Integer) ventNativas.get(aActWinTittle);
        if (tmp != null) {
            actWinHandle = tmp.intValue();
            actWinTittle = aActWinTittle;
            actWin = true;
            return;
        } else {
            return;
        }
    }

    public void setActWinHandle(int handle) {
        String tittle = new String(getNativeWindowTittle(handle));
        if (!ventNativas.containsKey(tittle)) {
            actWinTittle = tittle;
            actWinHandle = handle;
            actWin = true;
            ventNativas.put(tittle, new Integer(handle));
        }
    }

    public static int getActWinHandle() {
        return actWinHandle;
    }

    public static boolean isNativeMode() {
        return nativeMode;
    }

    public static void setNativeMode(boolean aActNative) {
        nativeMode = aActNative;
    }

    public static boolean isActWin() {
        return actWin;
    }
    private static boolean windowSystem = false;
    private static boolean actWin = false;
    private static boolean nativeMode = true;
    static final String NATIVE_LIBRARY = "WNativeRobot.dll";
    private static HashMap ventNativas = new HashMap();
    private static String actWinTittle = null;
    private static int actWinHandle;

    static {
        if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
            windowSystem = true;
        } else {
            windowSystem = false;
        }
    }
}
